package com.cictec.middleware.commons.utils;

import java.util.ArrayList;
import java.util.List;

import com.cictec.middleware.commons.utils.gps.CoordinateConversion;
import com.cictec.middleware.commons.utils.gps.CoordinateConversion.UTMPoint;
import com.cictec.middleware.commons.utils.gps.GpsUtils;
import com.cictec.middleware.commons.utils.gps.PointUtils;
import com.cictec.middleware.core.entity.BusLineMapPoint;
import com.cictec.middleware.core.entity.pojo.ZoneFenceEntity;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineSegment;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.operation.buffer.BufferOp;
import com.vividsolutions.jts.operation.buffer.BufferParameters;

/**
 * 图形判定工具类
 * 	1、生成点、多边形、线路图形
 *  2、判定点是否在多边形（线路图形）内
 *  3、判定相对地图线路距离
 * @author ql
 */
public class ZoneUtils {

	/**
	 * 根据经纬度获取UTM坐标系该点的位置（根据坐标获取该点的几何图形）。
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static Geometry getPointGeometry(String lat, String lng) {
		return getPointGeometry(Double.parseDouble(lat), Double.parseDouble(lng));
	}
	/**
	 * 根据经纬度获取UTM坐标系该点的位置（根据坐标获取该点的几何图形）。
	 * 
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static Geometry getPointGeometry(double lat, double lng) {
		UTMPoint pbNow = CoordinateConversion.latLon2UTM(lat, lng);
		Coordinate c = new Coordinate(pbNow.getX(), pbNow.getY());
		Geometry geoPoint = new GeometryFactory().createPoint(c);
		return geoPoint;
	}

	/**
	 * 判定坐标是否在多边形（围栏）内
	 * @param points
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static boolean isInZoneFence(List<ZoneFenceEntity> points, String lat, String lng){
		return isInZoneFence(points, Double.parseDouble(lat), Double.parseDouble(lng));
	}
	
	/**
	 * 判定坐标是否在多边形（围栏）内
	 * @param points
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static boolean isInZoneFence(List<ZoneFenceEntity> points, double lat, double lng){
		boolean flag = false;
		if (points.size() > 0) {
			Geometry currentPoint = getPointGeometry(lat, lng);
			Geometry sbZone = createPolygonGeometry(points);
			if (sbZone.contains(currentPoint)) {
				flag = true;
			}
		}
		return flag;
	}
	
	
	/**
	 * 判定坐标是否在多边形（围栏）内
	 * @param points
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static boolean isInZoneFence(Geometry zoneFence, String lat, String lng){
		return isInZoneFence(zoneFence, Double.parseDouble(lat), Double.parseDouble(lng));
	}
	
	/**
	 * 判定坐标是否在多边形（围栏）内
	 * @param points
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static boolean isInZoneFence(Geometry zoneFence, double lat, double lng){
		if(zoneFence != null){
			Geometry currentPoint = getPointGeometry(lat, lng);
			if (zoneFence.contains(currentPoint)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 创建一个多边形图形
	 * 
	 * @param points 围栏坐标点
	 * @return
	 */
	public static Geometry createPolygonGeometry(List<ZoneFenceEntity> points) {
		if (points.size() > 0) {

			Coordinate[] zoneCordinates = new Coordinate[points.size() + 1];

			for (int i = 0; i < points.size(); i++) {
				ZoneFenceEntity point = points.get(i);
				UTMPoint p = CoordinateConversion.latLon2UTM(Double.valueOf(point.getLat()), Double.valueOf(point.getLng()));
				zoneCordinates[i] = new Coordinate(p.getX(), p.getY());
				if (i == 0) {
					zoneCordinates[zoneCordinates.length - 1] = new Coordinate(p.getX(), p.getY());
				}
			}
			Polygon sbZone = new GeometryFactory().createPolygon(zoneCordinates);
			return sbZone;
		}
		return null;
	}
	
	/** 生成线路图形缓冲区 大小 */
	public static Double maxOffLine = (double) 100;
	
	/**
	 * 创建一个线路图形(默认缓冲区大小 100)
	 * @param points
	 * @return
	 */
	public static Geometry createLineStringGeometry(List<BusLineMapPoint> points) {
		return createLineStringGeometry(points, maxOffLine);
	}
	
	/**
	 * 创建一个线路图形
	 * @param points
	 * @param distance the buffer distance	缓冲区大小
	 * @return Returns the buffer computed for a geometry for a given buffer distance.
	 */
	public static Geometry createLineStringGeometry(List<BusLineMapPoint> points, double distance){
		Coordinate[] coordinates = new Coordinate[points.size()];
		for (int i = 0; i < points.size(); i++) {
			BusLineMapPoint ml = points.get(i);
			UTMPoint up = CoordinateConversion.latLon2UTM(Double.valueOf(ml.getMlLat()), Double.valueOf(ml.getMlLng()));
			coordinates[i] = new Coordinate(up.getX(), up.getY());
		}
		Geometry gline = new GeometryFactory().createLineString(coordinates);
		BufferOp pyBufferOp = new BufferOp(gline);
		pyBufferOp.setEndCapStyle(BufferParameters.CAP_ROUND);
		Geometry glineBuffer = pyBufferOp.getResultGeometry(distance);
		return glineBuffer;
	}
	
	/**
	 * 是否在线路图形内部(不在标识偏线)
	 * @param lineStringGeometry
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static boolean isInLineStringGeometry(Geometry lineStringGeometry, double lat, double lng){
		return lineStringGeometry.contains(getPointGeometry(lat, lng));
	}
	
	/**
	 * 是否在线路图形内部(不在表示偏线)
	 * @param lineStringGeometry
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static boolean isInLineStringGeometry(Geometry lineStringGeometry, String lat, String lng){
		if(lineStringGeometry == null){
			return false;
		}
		return lineStringGeometry.contains(getPointGeometry(lat, lng));
	}
	
	/** 是否在线路图形内部(不在表示偏线) */
	public static boolean isInLineStringGeometry(List<BusLineMapPoint> points, String lat, String lng){
		return createLineStringGeometry(points).contains(getPointGeometry(lat, lng));
	}
	
	/** 圆形站点缓冲区*/
	public static int STATION_BUFFER = 30;		// 
	/** 多边形站点缓冲区 */
	private static int STATION_LR_BUFFER = 50;  // 

	/**
	 * 获取点（站点）（一个坐标情况下）的圆形的缓冲区
	 * 
	 * @param currentStation
	 * @return
	 */
	public static Geometry createCircleBufferGeometry(String latitude, String longitude) {
		// 中间件内部统一使用火星坐标系
		UTMPoint enterUTM = CoordinateConversion.latLon2UTM(latitude, longitude);
		Geometry stationBuffer = createCircle(enterUTM.getX(), enterUTM.getY(), STATION_BUFFER);
		return stationBuffer;
	}
	
	/**
	 * 获取点（站点）（一个坐标情况下）的矩形的缓冲区。
	 * @param lsSequence 线路站点序号
	 * @param points	 线路站点左右相邻点(站点的话最多三个点，首末站可能只有2个)
	 * @return
	 */
	public static Geometry createRectBufferGeometry(int lsSequence, List<BusLineMapPoint> points) {
		List<Coordinate> csList = new ArrayList<Coordinate>();

		for (int i = 0; i < points.size(); i++) {
			BusLineMapPoint stationPoint = points.get(i);
			if (stationPoint.getMlWhichstop() == lsSequence) {
				Coordinate stationCoordinate = new Coordinate(stationPoint.getUtmPoint().getX(), stationPoint.getUtmPoint().getY());
				// 进站夹角计算
				if (i > 0) {
					// 站点前一个地图坐标点
					BusLineMapPoint ml = points.get(i - 1);
					// 确定下一个点是站点。
					LineSegment enterLine = new LineSegment(stationCoordinate, new Coordinate(ml.getUtmPoint().getX(), ml.getUtmPoint().getY()));
					csList.add(enterLine.pointAlong(STATION_BUFFER / enterLine.getLength()));
				}
				csList.add(stationCoordinate);
				// 出站夹角计算
				if (i + 1 < points.size()) {
					// 站点后的一个地图坐标点。
					BusLineMapPoint ml = points.get(i + 1);
					LineSegment outLine = new LineSegment(stationCoordinate, new Coordinate(ml.getUtmPoint().getX(), ml.getUtmPoint().getY()));
					csList.add(outLine.pointAlong(STATION_BUFFER / outLine.getLength()));

				}
				break;
			}
		}
		Coordinate[] csArray = new Coordinate[csList.size()];
		for (int i = 0; i < csList.size(); i++) {
			csArray[i] = csList.get(i);
		}
		Geometry gline = new GeometryFactory().createLineString(csArray);
		BufferOp pyBufferOp = new BufferOp(gline);
		pyBufferOp.setEndCapStyle(BufferParameters.CAP_FLAT);

		Geometry stationBuffer = pyBufferOp.getResultGeometry(STATION_LR_BUFFER);
		return stationBuffer;
	}
	
	/**
	 * 创建一个圆形图形
	 * @param x
	 * @param y
	 * @param radius
	 * @return
	 */
	public static Polygon createCircle(double x, double y, final double radius) {
		final int SIDES = 32;// 圆上面的点个数
		Coordinate coords[] = new Coordinate[SIDES + 1];
		for (int i = 0; i < SIDES; i++) {
			double angle = ((double) i / (double) SIDES) * Math.PI * 2.0;
			double dx = Math.cos(angle) * radius;
			double dy = Math.sin(angle) * radius;
			coords[i] = new Coordinate((double) x + dx, (double) y + dy);
		}
		coords[SIDES] = coords[0];
		GeometryFactory geometryFactory = new GeometryFactory();
		LinearRing ring = geometryFactory.createLinearRing(coords);
		Polygon polygon = geometryFactory.createPolygon(ring, null);
		return polygon;
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	/**
	 * 计算相对距离
	 * @param points	线路图形位置点信息
	 * @param latitude
	 * @param longitude
	 * @param maxLeng	线路图形最大长度
	 * @return
	 */
	public static double relativeValue(List<BusLineMapPoint> points, double latitude, double longitude, double maxLeng) {
		UTMPoint utmPoint = CoordinateConversion.latLon2UTM(latitude, longitude);
		return relativeValue(points, utmPoint, maxLeng);
	}
	
	/**
	 * 计算相对距离
	 * @param points	线路图形位置点信息
	 * @param latitude
	 * @param longitude
	 * @param maxLeng	线路图形最大长度
	 * @return
	 */
	public static double relativeValue(List<BusLineMapPoint> points, String latitude, String longitude, double maxLeng) {
		UTMPoint utmPoint = CoordinateConversion.latLon2UTM(latitude, longitude);
		return relativeValue(points, utmPoint, maxLeng);
	}
	
	/**
	 * 计算相对距离
	 * 
	 * @param points	线路地图点信息
	 * @param curPoint	当前点的UTM坐标
	 * @param maxLeng 	线段长度
	 * @return
	 */
	public static double relativeValue(List<BusLineMapPoint> points, UTMPoint curPoint, double maxLeng) {
		
		if (maxLeng == 0 || points.size() == 0) {
			return 0;
		}
		List<UTMPoint> ups = new ArrayList<UTMPoint>();
		for (int i = 0; i < points.size(); i++) {
			BusLineMapPoint ml = points.get(i);
			UTMPoint up = CoordinateConversion.latLon2UTM(Double.valueOf(ml.getMlLat()), Double.valueOf(ml.getMlLng()));
			ups.add(up);
		}
		if (points.size() == 1) {
			// 一个点的话根据该点计算相对距离
			return points.get(0).getMlDistance() / maxLeng;
		} else if (points.size() == 2) {
			// 两个点的时候
			UTMPoint vPoint = PointUtils.compute(ups.get(0), ups.get(1), curPoint);
			boolean inLine = PointUtils.pointInLine(ups.get(0), ups.get(1), curPoint);
			if (inLine) {
				double lv = GpsUtils.calcTwoUtmPointMiles(ups.get(0), vPoint);
				return (points.get(0).getMlDistance() + lv) / maxLeng;
			} else {
				double la = GpsUtils.calcTwoUtmPointMiles(ups.get(0), curPoint);
				double lb = GpsUtils.calcTwoUtmPointMiles(ups.get(1), curPoint);
				if (la < lb) {
					return points.get(0).getMlDistance() / maxLeng;
				} else {
					return points.get(1).getMlDistance() / maxLeng;
				}
			}

		} else {
			// 大于两个点
			UTMPoint vPoint1 = PointUtils.compute(ups.get(0), ups.get(1), curPoint);
			boolean inLine1 = PointUtils.pointInLine(ups.get(0), ups.get(1), vPoint1);

			UTMPoint vPoint2 = PointUtils.compute(ups.get(1), ups.get(2), curPoint);
			boolean inLine2 = PointUtils.pointInLine(ups.get(1), ups.get(2), vPoint2);

			if (inLine1 && inLine2) {
				// 两个垂直点都在线上
				double lv1 = GpsUtils.calcTwoUtmPointMiles(vPoint1, curPoint);
				double lv2 = GpsUtils.calcTwoUtmPointMiles(vPoint2, curPoint);

				if (lv1 < lv2) {
					double dv = GpsUtils.calcTwoUtmPointMiles(ups.get(0), vPoint1);
					return (points.get(0).getMlDistance() + dv) / maxLeng;
				} else {
					double dv = GpsUtils.calcTwoUtmPointMiles(ups.get(1), vPoint2);
					return (points.get(1).getMlDistance() + dv) / maxLeng;
				}

			} else if (inLine1 && !inLine2) {
				//
				double lv1 = GpsUtils.calcTwoUtmPointMiles(vPoint1, curPoint);
				double lc = GpsUtils.calcTwoUtmPointMiles(ups.get(2), curPoint);
				if (lv1 < lc) {
					double dv = GpsUtils.calcTwoUtmPointMiles(ups.get(0), vPoint1);
					return (points.get(0).getMlDistance() + dv) / maxLeng;
				} else {
					return (points.get(2).getMlDistance()) / maxLeng;
				}
			} else if (!inLine1 && inLine2) {
				//
				double lv2 = GpsUtils.calcTwoUtmPointMiles(vPoint2, curPoint);
				double la = GpsUtils.calcTwoUtmPointMiles(ups.get(0), curPoint);
				if (lv2 < la) {
					double dv = GpsUtils.calcTwoUtmPointMiles(ups.get(1), vPoint1);
					return (points.get(1).getMlDistance() + dv) / maxLeng;
				} else {
					return (points.get(0).getMlDistance()) / maxLeng;
				}
			} else {
				double la = GpsUtils.calcTwoUtmPointMiles(ups.get(0), curPoint);
				double lb = GpsUtils.calcTwoUtmPointMiles(ups.get(1), curPoint);
				double lc = GpsUtils.calcTwoUtmPointMiles(ups.get(2), curPoint);
				if (la <= lb && la <= lc) {
					return (points.get(0).getMlDistance()) / maxLeng;
				} else if (lb <= la && lb <= lc) {
					return (points.get(1).getMlDistance()) / maxLeng;
				} else {
					return (points.get(2).getMlDistance()) / maxLeng;
				}
			}

		}
		// return 0;
	}
	
}
